package cn.tedu.bece.admin.system.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.Header;
import cn.hutool.http.HttpUtil;
import cn.hutool.http.useragent.UserAgent;
import cn.hutool.http.useragent.UserAgentUtil;
import cn.hutool.json.JSONUtil;
import cn.tedu.bece.admin.common.service.CaptchaService;
import cn.tedu.bece.admin.common.util.SecurityUtils;
import cn.tedu.bece.admin.common.constant.Constants;
import cn.tedu.bece.admin.common.exception.ServiceException;
import cn.tedu.bece.admin.system.mapper.SysLoginLogMapper;
import cn.tedu.bece.admin.system.mapper.SysMenuMapper;
import cn.tedu.bece.admin.system.mapper.SysRoleMapper;
import cn.tedu.bece.admin.system.mapper.SysUserMapper;
import cn.tedu.bece.admin.system.pojo.dto.SysLoginParam;
import cn.tedu.bece.admin.system.pojo.entity.SysLoginLog;
import cn.tedu.bece.admin.system.pojo.entity.SysUser;
import cn.tedu.bece.admin.system.pojo.vo.LoginResultVO;
import cn.tedu.bece.admin.system.service.ISysLoginService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.util.Date;
import java.util.List;
import java.util.Objects;

@Service
@Slf4j
public class SysLoginServiceImpl implements ISysLoginService {

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysMenuMapper sysMenuMapper;

    @Autowired
    private SysLoginLogMapper sysLoginLogMapper;

    @Autowired
    private CaptchaService captchaService;

    @Override
    public LoginResultVO login(SysLoginParam sysLoginParam) {
        // 校验验证码
        captchaService.verifyCaptcha(sysLoginParam.getCaptchaKey(), sysLoginParam.getCaptcha());
        // 根据账号查询用户
        String account = sysLoginParam.getAccount();
        SysUser user = sysUserMapper.selectUserByUsername(account);
        // 判断账号是否不存在
        if (Objects.isNull(user)) {
            asyncInsertSysLoginLog(account, Constants.LOGIN_FAILED, "账号不存在");
            throw new ServiceException("账号或密码错误");
        }
        // 判断密码是否错误
        if (!SecurityUtils.matchesPassword(sysLoginParam.getPassword(), user.getPassword())) {
            asyncInsertSysLoginLog(account, Constants.LOGIN_FAILED, "密码不正确");
            throw new ServiceException("账号或密码错误");
        }
        // 判断账号是否被停用
        if (user.getStatus() == Constants.STATUS_DISABLED) {
            asyncInsertSysLoginLog(account, Constants.LOGIN_FAILED, "账号被停用");
            throw new ServiceException("账号已经被停用");
        }
        // 查询用户的角色
        List<String> roles = sysRoleMapper.selectRolesByUserId(user.getId());
        // 查询用户的权限
        List<String> perms = sysMenuMapper.selectPermsByUserId(user.getId());
        // sa-token 登录
        StpUtil.login(user.getId());
        StpUtil.getSession().set("roles", roles);
        StpUtil.getSession().set("perms", perms);
        StpUtil.getSession().set("username", user.getUsername());
        // 更新登录ip和时间
        asyncUpdateLoginIpAndTime(user.getId());
        // 插入登录日志
        asyncInsertSysLoginLog(account, Constants.LOGIN_SUCCESS, "登录成功");
        // 返回 LoginResultVO
        LoginResultVO loginResultVO = new LoginResultVO();
        loginResultVO.setNickname(user.getNickname());
        loginResultVO.setAvatar(user.getAvatar());
        loginResultVO.setPerms(perms);
        loginResultVO.setToken(StpUtil.getTokenValue());
        return loginResultVO;
    }

    private void asyncUpdateLoginIpAndTime(Long userId) {
        String ip = ServletUtil.getClientIP(
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest());
        ThreadUtil.execute(() -> {
            SysUser sysUser = new SysUser();
            sysUser.setId(userId);
            sysUser.setLoginIp(ip);
            sysUser.setLoginTime(new Date());
            sysUserMapper.updateById(sysUser);
        });
    }

    private void asyncInsertSysLoginLog(String username, Integer status, String msg) {
        HttpServletRequest request =
                ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String ip = ServletUtil.getClientIP(request);
        UserAgent userAgent = UserAgentUtil.parse(request.getHeader(Header.USER_AGENT.getValue()));
        ThreadUtil.execute(() -> {
            SysLoginLog sysLoginLog = new SysLoginLog();
            sysLoginLog.setUsername(username);
            sysLoginLog.setIp(ip);
            sysLoginLog.setLocation(getLocationByIp(ip));
            sysLoginLog.setBrowser(userAgent.getBrowser().getName());
            sysLoginLog.setOs(userAgent.getOs().getName());
            sysLoginLog.setStatus(status);
            sysLoginLog.setMsg(msg);
            sysLoginLog.setLoginTime(new Date());
            sysLoginLogMapper.insert(sysLoginLog);
        });
    }

    private String getLocationByIp(String ip) {
        try {
            InetAddress inetAddress = InetAddress.getByName(ip);
            if (inetAddress.isAnyLocalAddress() ||
                    inetAddress.isLoopbackAddress() ||
                    inetAddress.isLinkLocalAddress() ||
                    inetAddress.isSiteLocalAddress()
            ) {
                return "内网IP";
            }
            String url = "http://whois.pconline.com.cn/ipJson.jsp?ip=" + ip + "&json=true";
            String response = HttpUtil.get(url);
            return JSONUtil.parseObj(response).getStr("addr");
        } catch (Exception e) {
            log.error("获取{}地理位置异常 {}", ip, e.getMessage());
        }
        return "Unknown";
    }

}
